// $Id: simple.cpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#include <boost/test/unit_test.hpp>
#include <xpcom/object.hpp>
#include <xpcom/module.hpp>
#include <xpcom/ptr.hpp>
#include "interface_def.hpp"

using namespace xpcom;

namespace {
int count = 0;

struct AA
{
    int n1;
    long n2;
    double n3;
};

class X : public AA, public xpcom::object<X, IA, IC>
{
public:
    X() : n_(0), a_(0), b_(0), c_(0)
    {
        ++count;
    }

    refcnt_t ref() { return 0; }

    int getN() { return n_; }
    void setN(int n) { n_ = n; }

    int getA() { return a_; }
    void setA(int a) { a_ = a; }

    int getB() { return b_; }
    void setB(int b) { b_ = b; }

    int getC() { return c_; }
    void setC(int c) { c_ = c; }

protected:
    ~X() { --count; }

private:
    int n_, a_, b_, c_;
};

class Y : public inherited_object<Y, X, ID>
{
public:
    Y() : d_(0)
    {
    }

    int getD() { return d_; }
    void setD(int d) { d_ = d; }

protected:
    ~Y() {}

private:
    int d_;
};

class Z : public aggregatable<single_threaded_object<Z, IBase> >
{
public:
    Z() : n_(0)
    {
        ++count;
    }

    refcnt_t ref() { return 0; }

    int getN() { return n_; }
    void setN(int n) { n_ = n; }

protected:
    // Not on stack
    ~Z()
    {
        --count;
    }

private:
    int n_;
};

class simple_module : public module<X, Y, Z>
{
public:
    simple_module()
    {
        component<X>()
            .classid("{8ECDCD45-5774-474e-8164-AB706E563804}")
            .contractid("X@test.org;1")
            .description("X");

        component<Y>()
            .classid("08A669EE-DFD8-44ce-ADE6-03DC179639A8")
            .contractid("Y@test.org;1")
            .description("Y")
            .factory_model<factory_model::singleton_factory_tag>();

        component<Z>()
            .classid("{88AEB9D4-2FFF-4147-92AF-04C7971BDF77}");
    }
};

void do_test_x()
{
    using namespace xpcom;

    ref_ptr<X> p(new X);

    ref_ptr<IB> p1 = dynamic_pointer_cast<IB>(p);
    BOOST_CHECK(p1);
    p1->setB(10);
    p1->setN(100);
    BOOST_CHECK_EQUAL(p1->getB(), 10);

    ref_ptr<IA> p2 = dynamic_pointer_cast<IA>(p1);
    BOOST_CHECK(p2);
    p2->setA(8);
    BOOST_CHECK_EQUAL(p2->getA(), 8);

    ref_ptr<IC> p3 = dynamic_pointer_cast<IC>(p2);
    BOOST_CHECK(p3);
    p3->setC(90);
    BOOST_CHECK_EQUAL(p3->getC(), 90);
    BOOST_CHECK_EQUAL(p3->getB(), 10);
    BOOST_CHECK_EQUAL(p3->getN(), 100);

    ref_ptr<IBase> p4 = dynamic_pointer_cast<IBase>(p);
    BOOST_CHECK(p4);
    BOOST_CHECK_EQUAL(p4->getN(), 100);
    p4->setN(200);
    BOOST_CHECK_EQUAL(p1->getN(), 200);
    BOOST_CHECK_EQUAL(p2->getN(), 200);
    BOOST_CHECK_EQUAL(p3->getN(), 200);

    ref_ptr<ID> p5 = dynamic_pointer_cast<ID>(p1);
    BOOST_CHECK(!p5);
}

void do_test_y()
{
    using namespace xpcom;

    ref_ptr<X> p(new Y);

    ref_ptr<IB> p1 = dynamic_pointer_cast<IB>(p);
    BOOST_CHECK(p1);
    p1->setB(10);
    p1->setN(100);
    BOOST_CHECK_EQUAL(p1->getB(), 10);

    ref_ptr<IA> p2 = dynamic_pointer_cast<IA>(p1);
    BOOST_CHECK(p2);
    p2->setA(8);
    BOOST_CHECK_EQUAL(p2->getA(), 8);

    ref_ptr<IC> p3 = dynamic_pointer_cast<IC>(p2);
    BOOST_CHECK(p3);
    p3->setC(90);
    BOOST_CHECK_EQUAL(p3->getC(), 90);
    BOOST_CHECK_EQUAL(p3->getB(), 10);
    BOOST_CHECK_EQUAL(p3->getN(), 100);

    ref_ptr<IBase> p4 = dynamic_pointer_cast<IBase>(p);
    BOOST_CHECK(p4);
    BOOST_CHECK_EQUAL(p4->getN(), 100);
    p4->setN(200);
    BOOST_CHECK_EQUAL(p1->getN(), 200);
    BOOST_CHECK_EQUAL(p2->getN(), 200);
    BOOST_CHECK_EQUAL(p3->getN(), 200);

    ref_ptr<ID> p5 = dynamic_pointer_cast<ID>(p1);
    BOOST_CHECK(p5);
    p5->setD(800);
    BOOST_CHECK_EQUAL(p5->getD(), 800);
}

}

BOOST_AUTO_TEST_CASE(test_simple_x)
{
    do_test_x();
    BOOST_CHECK_EQUAL(count, 0);
}

BOOST_AUTO_TEST_CASE(test_simple_y)
{
    do_test_y();
    BOOST_CHECK_EQUAL(count, 0);
}

BOOST_AUTO_TEST_CASE(test_module_1)
{
    count = 0;
    simple_module sm;
    module_base* m = static_cast<module_base*>(&sm);

    // {8ECDCD45-5774-474e-8164-AB706E563804}
    const uuid idx =
    { 0x8ecdcd45, 0x5774, 0x474e, { 0x81, 0x64, 0xab, 0x70, 0x6e, 0x56, 0x38, 0x4 } };

    // {08A669EE-DFD8-44ce-ADE6-03DC179639A8}
    const uuid idy =
    { 0x8a669ee, 0xdfd8, 0x44ce, { 0xad, 0xe6, 0x3, 0xdc, 0x17, 0x96, 0x39, 0xa8 } };

    // {88AEB9D4-2FFF-4147-92AF-04C7971BDF77}
    const uuid idz =
    { 0x88aeb9d4, 0x2fff, 0x4147, { 0x92, 0xaf, 0x4, 0xc7, 0x97, 0x1b, 0xdf, 0x77 } };

    BOOST_CHECK_EQUAL(m->component_count_, 3);

    // X
    BOOST_CHECK(m->components_[0]->classid_ == idx);
    BOOST_CHECK(m->components_[0]->contractid_ == "X@test.org;1");
    BOOST_CHECK(m->components_[0]->description_ == "X");
    BOOST_CHECK(m->components_[0]->flags_ == module_component_base::flag_threadsafe);
    BOOST_CHECK(m->components_[0]->factory_ == module_component_base::standard_factory);

    // Y
    BOOST_CHECK(m->components_[1]->classid_ == idy);
    BOOST_CHECK(m->components_[1]->contractid_ == "Y@test.org;1");
    BOOST_CHECK(m->components_[1]->description_ == "Y");
    BOOST_CHECK(m->components_[1]->flags_ == (module_component_base::flag_threadsafe | module_component_base::flag_singleton));
    BOOST_CHECK(m->components_[1]->factory_ == module_component_base::singleton_factory);

    // Z
    BOOST_CHECK(m->components_[2]->classid_ == idz);
    BOOST_CHECK(m->components_[2]->contractid_.empty());
    BOOST_CHECK(m->components_[2]->description_.empty());
    BOOST_CHECK(m->components_[2]->flags_ == 0);
    BOOST_CHECK(m->components_[2]->factory_ == module_component_base::aggregatable_factory);

    BOOST_CHECK_EQUAL(count, 0);
}
